﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace nameero.util
{
    class GameState
    {
        //polja na ploči, 0 je slobodno polje, 1 je igrač, -1 je računalo, dok su kraljevi 2 i -2
        private sbyte[,] boardSquares;
        //potez koji je doveo do tog stanja
        private String movIn;
        private Point startPoint, endPoint;
        private Heuristics heuristics;
        public LinkedList<GameState> nextJumps;

        public GameState()
        {
            heuristics = new Heuristics();
            boardSquares = new sbyte[8, 8];
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    if (i <= 2 && (i + j) % 2 == 0)
                        boardSquares[i, j] = -1;
                    else if (i >= 5 && (i + j) % 2 == 0)
                        boardSquares[i, j] = 1;
                    else
                        boardSquares[i, j] = 0;
                }
            }
            
            //namještanje testne situacije.. maknuti!!!
            boardSquares[2, 2] = 0;
            boardSquares[3, 3] = -1;
            boardSquares[7, 3] = 0;
            boardSquares[7, 7] = 0;
            nextJumps = new LinkedList<GameState>();

            // dodo Dax, radi početnog JumpAround()
            startPoint = new Point(0, 0);
            endPoint = new Point(3, 3);
        }

        public GameState(GameState state)
        {
            heuristics = new Heuristics(state.heuristics);
            boardSquares = new sbyte[8, 8];
            Array.Copy(state.boardSquares, this.boardSquares, state.boardSquares.Length);
            nextJumps = new LinkedList<GameState>();
            this.movIn = state.movIn;
            if(state.startPoint != null)
                this.startPoint = new Point(state.startPoint.row, state.startPoint.col);
            if(state.endPoint != null)
                this.endPoint = new Point(state.startPoint.row, state.startPoint.col);
        }

        public GameState doMove(Point from, Point to, bool player1)
        {
            GameState newGameState = new GameState(this);
            heuristics.evaluateBeforeMove(newGameState, from, to);
            newGameState.boardSquares[to.row, to.col] = newGameState.boardSquares[from.row, from.col];
            newGameState.boardSquares[from.row, from.col] = 0;
            if (Math.Abs(from.row - to.row) == 2 && Math.Abs(from.col - to.col) == 2)
            {
                int rowErase = from.row + (to.row - from.row) / 2;
                int colErase = from.col + (to.col - from.col) / 2;
                newGameState.heuristics.evaluateErased(newGameState,new Point(rowErase, colErase));
                newGameState.boardSquares[rowErase, colErase] = 0;
            }
            //ako je došao do druge strane postaje kralj
            if (to.row == 0 && newGameState.boardSquares[to.row, to.col] == 1 || to.row == 7 && newGameState.boardSquares[to.row, to.col] == -1)
                newGameState.boardSquares[to.row, to.col] *= 2;
            heuristics.evaluateAfterMove(newGameState, to);
            newGameState.SetMovIn(from, to);
            return newGameState;
        }


        public LinkedList<GameState> getAllMoves(bool player1)
        {
            LinkedList<GameState> allGameStates = new LinkedList<GameState>();
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    if (player1 && boardSquares[i, j] > 0 || !player1 && boardSquares[i, j] < 0)
                    {
                        LinkedList<GameState> allStates = getJumpMoves(new Point(i, j),player1);
                        foreach (GameState g in allStates)
                            allGameStates.AddLast(g);

                    }
                }
            }

            if (allGameStates.Count == 0) //ako ne možemo skakati, onda dodajemo "obične" poteze
            {
                for (int i = 0; i < 8; i++)
                {
                    for (int j = 0; j < 8; j++)
                    {
                        Point current = new Point(i, j);
                        if (player1 && boardSquares[i, j] > 0)
                        {
                            if (isFree(new Point(i - 1, j + 1)))
                            {
                                allGameStates.AddLast(doMove(current, new Point(i - 1, j + 1),player1)); //gore desno
                            }
                            if (isFree(new Point(i - 1, j - 1)))
                            {
                                allGameStates.AddLast(doMove(current, new Point(i - 1, j - 1),player1)); //gore lijevo
                            }
                            if (boardSquares[i, j] == 2) //ako je kralj
                            {
                                if (isFree(new Point(i + 1, j + 1)))
                                {
                                    allGameStates.AddLast(doMove(current, new Point(i + 1, j + 1),player1)); //dolje desno
                                }
                                if (isFree(new Point(i + 1, j - 1)))
                                {
                                    allGameStates.AddLast(doMove(current, new Point(i + 1, j - 1),player1)); //dolje lijevo
                                }
                            }
                        }
                        if (!player1 && boardSquares[i, j] < 0)
                        {
                            if (isFree(new Point(i + 1, j + 1)))
                            {
                                allGameStates.AddLast(doMove(current, new Point(i + 1, j + 1),player1)); //dolje desno
                            }
                            if (isFree(new Point(i + 1, j - 1)))
                            {
                                allGameStates.AddLast(doMove(current, new Point(i + 1, j - 1),player1)); //dolje lijevo
                            }
                            if (boardSquares[i, j] == -2) //ako je kralj
                            {
                                if (isFree(new Point(i - 1, j + 1)))
                                {
                                    allGameStates.AddLast(doMove(current, new Point(i - 1, j + 1),player1)); //gore desno
                                }
                                if (isFree(new Point(i - 1, j - 1)))
                                {
                                    allGameStates.AddLast(doMove(current, new Point(i - 1, j - 1),player1)); //gore lijevo
                                }
                            }
                        }
                    }
                }
            }

            return allGameStates;
        }

        public LinkedList<GameState> getJumpMoves(Point p,bool player1)
        {
            LinkedList<GameState> jumpMoves = new LinkedList<GameState>();
            int typeOfPiece = boardSquares[p.row, p.col];
            if (typeOfPiece > 0 && player1 || typeOfPiece==-2 && !player1)
            {
                if (canJump(p, new Point(p.row - 1, p.col + 1))) //skok preko pločice gore desno
                {
                    GameState newGameState = doMove(p, new Point(p.row - 2, p.col + 2),player1);
                    //newGameState.removePiece(p.row - 1, p.col + 1);
                    //newGameState.boardSquares[p.row - 1, p.col + 1] = 0;
                    jumpMoves.AddLast(newGameState);
                    if (!(typeOfPiece == 1 && p.row - 2 == 0))
                    {
                        LinkedList<GameState> listOfJumps = newGameState.getJumpMoves(new Point(p.row - 2, p.col + 2), player1);
                        foreach (GameState g in listOfJumps)
                            jumpMoves.AddLast(g);
                    }
                    nextJumps.AddLast(newGameState);
                }
                if (canJump(p, new Point(p.row - 1, p.col - 1))) //skok preko pločice gore lijevo
                {
                    GameState newGameState = doMove(p, new Point(p.row - 2, p.col - 2),player1);
                    //newGameState.removePiece(p.row - 1, p.col - 1);
                    //newGameState.boardSquares[p.row - 1, p.col - 1] = 0;
                    jumpMoves.AddLast(newGameState);
                    if (!(typeOfPiece == 1 && p.row - 2 == 0))
                    {
                        LinkedList<GameState> listOfJumps = newGameState.getJumpMoves(new Point(p.row - 2, p.col - 2), player1);
                        foreach (GameState g in listOfJumps)
                            jumpMoves.AddLast(g);
                    }
                    nextJumps.AddLast(newGameState);
                }
            }
            if (typeOfPiece < 0 && !player1 || typeOfPiece == 2 && player1)
            {
                if (canJump(p, new Point(p.row + 1, p.col + 1))) //skok preko pločice dolje desno
                {
                    GameState newGameState = doMove(p, new Point(p.row + 2, p.col + 2),player1);
                    //newGameState.removePiece(p.row + 1, p.col + 1);
                    //newGameState.boardSquares[p.row + 1, p.col + 1] = 0;
                    jumpMoves.AddLast(newGameState);
                    if (!(typeOfPiece == -1 && p.row + 2 == 7))
                    {
                        LinkedList<GameState> listOfJumps = newGameState.getJumpMoves(new Point(p.row + 2, p.col + 2), player1);
                        foreach (GameState g in listOfJumps)
                            jumpMoves.AddLast(g);
                    }
                    nextJumps.AddLast(newGameState);
                }
                if (canJump(p, new Point(p.row + 1, p.col - 1))) //skok preko pločice dolje lijevo
                {
                    GameState newGameState = doMove(p, new Point(p.row + 2, p.col - 2),player1);
                    //newGameState.removePiece(p.row + 1, p.col - 1);
                    //newGameState.boardSquares[p.row + 1, p.col - 1] = 0;
                    jumpMoves.AddLast(newGameState);
                    if (!(typeOfPiece == -1 && p.row + 2 == 7))
                    {
                        LinkedList<GameState> listOfJumps = newGameState.getJumpMoves(new Point(p.row + 2, p.col - 2), player1);
                        foreach (GameState g in listOfJumps)
                            jumpMoves.AddLast(g);
                    }
                    nextJumps.AddLast(newGameState);
                }
            }
            return jumpMoves;
        }

        private void removePiece(int p, int q)
        {
            GameState newState = new GameState(this);
            newState.boardSquares[p, q] = 0;
            this.boardSquares = newState.boardSquares;
        }

        private bool canJump(Point from, Point over)
        {
            return isFree(new Point(2 * over.row - from.row, 2 * over.col - from.col)) && isInside(over) && (Math.Sign(boardSquares[from.row, from.col]) == -Math.Sign(boardSquares[over.row, over.col]));
        }

        private bool isFree(Point p)
        {
            return isInside(p) && boardSquares[p.row, p.col] == 0;
        }

        private bool isInside(Point p)
        {
            return p.row >= 0 && p.row <= 7 && p.col >= 0 && p.col <= 7;
        }


        public sbyte[,] getBoard()
        {
            return boardSquares;
        }

        override
        public String ToString()
        {
            String s = "";
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    switch (boardSquares[i, j])
                    {
                        case 1: s += " o "; break;
                        case -1: s += " x "; break;
                        case 2: s += " O "; break;
                        case -2: s += " X "; break;
                        case 0: s += " . "; break;
                    }
                    if (j != 7)
                    {
                        s += "|";
                    }
                }
                s += "|\n";
                if (i != 7)
                {
                    s += "--------------------------------\n";
                }
            }
            return s;
        }

        public Heuristics getHeuristics()
        {
            return heuristics;
        }

        public void SetMovIn(Point from, Point to)
        {
            startPoint = from;
            endPoint = to;
            movIn = from.ToString() + " --> " + to.ToString();
        }

        public String GetMovIn()
        {
            return movIn;
        }

        public Point GetStartPoint()
        {
            return startPoint;
        }

        public Point GetEndPoint()
        {
            return endPoint;
        }

        
        public LinkedList<MultiMove> jumpAround() {
            LinkedList<MultiMove> ret = new LinkedList<MultiMove>();
            LinkedList<Point> p = new LinkedList<Point>();
            skaci(ret, this, p);
            return ret;
        }

        public void skaci(LinkedList<MultiMove> m, GameState s, LinkedList<Point> p)
        {
            if (Math.Abs(s.startPoint.row - s.endPoint.row) == 2)
            {
                if (p.Count == 0)
                    p.AddLast(s.startPoint);
                p.AddLast(s.endPoint);
            }
            if (s.nextJumps.Count == 0)
            {
                m.AddLast(new MultiMove(p));
                if (p.Count > 0) //dodo Dax
                    p.RemoveLast();
                else
                    m.RemoveLast();
            }
            
            foreach (GameState s1 in s.nextJumps)
                skaci(m, s1, p);
        }
    }
}
